/****************************************************************************
*  Title: top.cpp
*  Author: Bryant Smith
*  Date: 05/01/2011
*  Description: main process for part B, edge detection subset
****************************************************************************/

#include "systemc.h"
//#inculde <design.h>


#define IMG_ROWS 480
#define IMG_CLMS 640
#define IMG_HEADER 1078
#define TEST_IMG_COUNT 5

SC_MODULE(stimulus)
{
	sc_event stage2;

	sc_event recdImg;
	sc_event newImg;
	sc_fifo_out<char> inputImg;    
	
	void read_data()
	{
		// File names
		char INPUT_BMP[] = "Stereo/leftX.bmp"; // where X is replaed by file number
		char OUTPUT_BMP[] = "output/outputX.bmp"; // output file name
		char imgBufCount[1];

		// image buffers these are currently dependent on the size and format of the image used
		char header_buffer[IMG_HEADER]; // 1078 byte header including raster data for BW grayscale 8 bit
		char row_buffer_1[IMG_CLMS];
		
		int row, wt_cnt, imgCount, i;
		
		//File variable
		FILE * fptr;
		FILE * wptr;
		
		imgCount = 0;
		
		while(1)
		{		
			imgCount = imgCount ++;
			sprintf(imgBufCount, "%d", imgCount);
			
			// Determine Next image
			INPUT_BMP[11] = imgBufCount[0];
			OUTPUT_BMP[13] = imgBufCount[0];
			
			//********************
			// Start Processing //
			//********************			
			
			// Open files
			fptr = fopen(INPUT_BMP, "rb");
			wptr = fopen(OUTPUT_BMP, "w");
			
			
			if (fptr == NULL) 
			{
				printf("Cannot open %s\n", INPUT_BMP);
				exit(0);
			}			
			printf("Stimulus program - sending image %s\n", INPUT_BMP);
			
			// read header info (1078 bytes including raster data)
			fread (header_buffer,1,IMG_HEADER,fptr);
			
			// Write header out the output image file before starting processing
			// should be able to do this in one step, didn't work????
			for(wt_cnt = 0; wt_cnt < IMG_HEADER; wt_cnt++)
			{
					fprintf(wptr,"%c",header_buffer[wt_cnt]);
			}
			fclose(wptr);
			
//			newImg.notify();  // or replace with a signal?
			
			// read file and send to queue one row at a time
			for(row = 0; row < IMG_ROWS; row++)
			{
				// Load buffer with a row of the image
				fread (row_buffer_1,1,(IMG_CLMS),fptr); 
				
				// send row over fifo
				for( i = 0; i < IMG_CLMS; i++) {
					inputImg.write(row_buffer_1[i]);
				}
			}
			
			fclose(fptr);
		}
	}
	
	SC_CTOR(stimulus)
	{
		SC_THREAD(read_data);
	}
};


SC_MODULE(design)
{	
	sc_event stage2;

	sc_event newImg;
	sc_event recdImg;
	sc_event procdImg;
	sc_fifo_in<char> inputImg;    
	sc_fifo_out<char> outputImg;

	unsigned char image_buffer[IMG_ROWS][IMG_CLMS];
	double sobelG[IMG_ROWS][IMG_CLMS];
	
	void readData()
	{
	printf("started data reader\n");
		
		// receiver vars
		unsigned char row_buffer_1[IMG_CLMS]; // this file is 4x4 since there are 3 bytes/pixel -> 1536 bytes per row
		int row, a, j;
		
		while(1)
		{
			// read image from fifo and load into image buffer
			for(row = 0; row < IMG_ROWS; row++) {
				
				// place row into image buffer fifo
				for(a = 0; a < (IMG_CLMS); a++){
					image_buffer[row][a] = inputImg.read();				
				}			
			}		
			printf("Image Processor - Finished reading image into memory.  Starting Image Processing . . . .\n");
			
			recdImg.notify();
			stage2.notify();
		}
	}
	
	
	void edgeDetect()
	{
		// sobel vars
		int masksize = 3;
		int sobelX[3][3] = {{-1, 0, 1}, {-2, 0, 2}, {-1, 0, 1}};
		int sobelY[3][3] = {{1, 2, 1}, {0, 0, 0}, {-1, -2, -1}};
		int x, y, x_1, y_1, posx, posy, imPosx, imPosy;
		int width, height;
		double valx, valy;
		
		while(1)
		{
			wait(recdImg);
			
			width = IMG_CLMS;
			height = IMG_ROWS;
			//********************
			// Apply sobel mask
			//********************			
			for(x = masksize / 2; x < width - (masksize / 2); x++) {  //columns
				for(y= masksize / 2; y < height- (masksize / 2); y++) { // rows
					valx = 0;
					valy = 0;
					for( x_1 = 0; x_1 < masksize; x_1++) {
						for( y_1 = 0; y_1 < masksize; y_1++) {
							imPosx = (x + (x_1 - (masksize / 2)));
							imPosy = (y + (y_1 - (masksize / 2)));
							
							valx += ((image_buffer[imPosy][imPosx]) * sobelX[y_1][x_1]);
							valy += ((image_buffer[imPosy][imPosx]) * sobelY[y_1][x_1]);
							//printf("valx %f\n", valx);
						}
					}
					
					sobelG[y][x] =  sqrt(valx * valx + valy * valy);  // gradient magnitude		
				}
			}
			procdImg.notify();
		}
	}
	
	
	void sendResult()
	{
		// normalize
		int row, wt_cnt, i, j;
		double val, max, normFact;
		unsigned char pixel;
		
		while(1)
		{
			wait(procdImg);
		
			// normalize not yet used 
			// find min an max value
			max = 0;
			//min = 0;
			for(i = 0; i < IMG_ROWS; i++) 
			{
				for(j = 0; j < IMG_CLMS; j++)
				{
					if (sobelG[i][j] > max) max = sobelG[i][j];
				}
			}
			
			normFact = max / 255;

			for(row = 0; row < IMG_ROWS; row++)
			{
				
				for(wt_cnt = 0; wt_cnt < IMG_CLMS; wt_cnt++)
				{
					pixel = (unsigned char) ((sobelG[row][wt_cnt]) / normFact);
					outputImg.write(pixel);
				}			
			}

			printf("Image Processor - Image has been sent out\n");
		}
	}
	

	SC_CTOR(design)
	{
		SC_THREAD(readData);   // stage 1
		//sensitive << newImg;
		SC_THREAD(edgeDetect); // stage 2
    	//sensitive << recdImg;
		SC_THREAD(sendResult); // stage 3
	    //sensitive << procdImg;
	}
};


SC_MODULE(monitor)
{
	sc_fifo_in<char> outputImg;  
	
	void write_out()
	{
		// File names
		char OUTPUT_BMP[] = "output/outputX.bmp"; // output file name
		
		// image buffers these are currently dependent on the size and format of the image used
		unsigned char row_buffer_1[IMG_CLMS];
		unsigned char image_buffer[IMG_ROWS][IMG_CLMS];
		char imgBufCount[1];
		
		int row, wt_cnt, i, j;
		
		//File variable
		FILE * wptr;
		
		for( i = 1; i <= 5; i++)
		{
			// Determine Next image
			sprintf(imgBufCount, "%d", i);
			OUTPUT_BMP[13] = imgBufCount[0];
		
			// open output file file
			wptr = fopen(OUTPUT_BMP, "a"); // append to file which already has header from stimulus

			for(row = 0; row < IMG_ROWS; row++)
			{
				// send a row of the image
				for( j = 0; j < IMG_CLMS; j++) {
					row_buffer_1[j] = outputImg.read();
				}
				
				for(wt_cnt = 0; wt_cnt < (IMG_CLMS); wt_cnt++)
				{
					fprintf(wptr,"%c",row_buffer_1[wt_cnt]);
				}
			}
			printf("Monitor - Writing Image %d\n", i);
		}		
  
	fclose(wptr);
	printf("Processing completed check file %s\n", OUTPUT_BMP);
	
	exit(0);
	}

	SC_CTOR(monitor)
	{
		SC_THREAD(write_out);
	}
};


SC_MODULE(top)
{		
	design *design_instance;
	stimulus *stimulus_instance;	
	monitor *monitor_instance;
	
	sc_fifo<char> *p_inputImg;
	sc_fifo<char> *p_outputImg;
	
	SC_CTOR(top) 
	{
		p_inputImg    = new sc_fifo<char>("inputImg");
		p_outputImg   = new sc_fifo<char>("outputImg");   

		stimulus_instance = new stimulus("stimulus");
		stimulus_instance -> inputImg(*p_inputImg);
		
		design_instance = new design("design");
		design_instance -> inputImg(*p_inputImg);
		design_instance -> outputImg(*p_outputImg);

		monitor_instance = new monitor("monitor");
		monitor_instance -> outputImg(*p_outputImg);
		
	}
};

int sc_main (int argc , char *argv[]) {
   top top1("top");
   sc_start();
   return 0;
}


